//     Zepto.js
//     (c) 2010-2016 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.
//
//     Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors

; (function ($) {
  var slice = Array.prototype.slice

  function Deferred(func) {
    var tuples = [
      // action, add listener, listener list, final state
      ["resolve", "done", $.Callbacks({ once: 1, memory: 1 }), "resolved"],
      ["reject", "fail", $.Callbacks({ once: 1, memory: 1 }), "rejected"],
      ["notify", "progress", $.Callbacks({ memory: 1 })]
    ],
      state = "pending",
      promise = {
        state: function () {
          return state
        },
        always: function () {
          deferred.done(arguments).fail(arguments)
          return this
        },
        then: function (/* fnDone [, fnFailed [, fnProgress]] */) {
          var fns = arguments
          return Deferred(function (defer) {
            $.each(tuples, function (i, tuple) {
              var fn = $.isFunction(fns[i]) && fns[i]
              deferred[tuple[1]](function () {
                var returned = fn && fn.apply(this, arguments)
                if (returned && $.isFunction(returned.promise)) {
                  returned.promise()
                    .done(defer.resolve)
                    .fail(defer.reject)
                    .progress(defer.notify)
                } else {
                  var context = this === promise ? defer.promise() : this,
                    values = fn ? [returned] : arguments
                  defer[tuple[0] + "With"](context, values)
                }
              })
            })
            fns = null
          }).promise()
        },

        promise: function (obj) {
          return obj != null ? $.extend(obj, promise) : promise
        }
      },
      deferred = {}

    $.each(tuples, function (i, tuple) {
      var list = tuple[2],
        stateString = tuple[3]

      promise[tuple[1]] = list.add

      if (stateString) {
        list.add(function () {
          state = stateString
        }, tuples[i ^ 1][2].disable, tuples[2][2].lock)
      }

      deferred[tuple[0]] = function () {
        deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments)
        return this
      }
      deferred[tuple[0] + "With"] = list.fireWith
    })

    promise.promise(deferred)
    if (func) func.call(deferred, deferred)
    return deferred
  }

  $.when = function (sub) {
    var resolveValues = slice.call(arguments),
      len = resolveValues.length,
      i = 0,
      remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0,
      deferred = remain === 1 ? sub : Deferred(),
      progressValues, progressContexts, resolveContexts,
      updateFn = function (i, ctx, val) {
        return function (value) {
          ctx[i] = this
          val[i] = arguments.length > 1 ? slice.call(arguments) : value
          if (val === progressValues) {
            deferred.notifyWith(ctx, val)
          } else if (!(--remain)) {
            deferred.resolveWith(ctx, val)
          }
        }
      }

    if (len > 1) {
      progressValues = new Array(len)
      progressContexts = new Array(len)
      resolveContexts = new Array(len)
      for (; i < len; ++i) {
        if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
          resolveValues[i].promise()
            .done(updateFn(i, resolveContexts, resolveValues))
            .fail(deferred.reject)
            .progress(updateFn(i, progressContexts, progressValues))
        } else {
          --remain
        }
      }
    }
    if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
    return deferred.promise()
  }

  $.Deferred = Deferred
})(Zepto)